在跟著手把手教學做了個簡單的應用程式後,應該會對Dart的語法有點基礎概念了吧。今天,筆者將整理一些dart基礎語法,讓未來寫flutter程式時可以更快速,少一些語法錯誤。本篇將會假設讀者已經會c語言,並省略部份與c相同內容的說明。
以下為此篇所參考的資料來源:
之前在做flutter專案時就已經在寫dart程式,且flutter的安裝並未特別要求我們需要先安裝dart sdk,各位應該都可以猜到其中的原因。沒錯,Flutter SDK中已經包含了Dart SDK,所以已經安裝了Flutter的人,可以不必再去安裝Dart了。若未下載flutter或是只想安裝dart的,這裡是官方下載說明文件。
Dart是個有點類似c語言的一種程式語言,因此在函式、變數宣告等地方都有點相似,且與c相同,每行執行的程式最後都須加上個分號;
。
main()
與c相同,每個程式都將會執行程式碼中的main()
函數,也是一個dart程式的必要項目。若程式需要,dart中的main
跟c一樣,可以加入arguments。
void main() {
print('Hello, World!');
}
變數的命名規則:
_
例外)Dart的變數有一個特別的地方,是宣告變數時不必特別指定他們的資料型態,只需要一個var
,便能涵蓋所有的變數。當然,若是想要,也可以指定資料型態。
關於dynamic
與Object
可參考此連結。
var someNum = 23423;
var anotherNum = 42.58;
var someBool = true;
var someString = "asdfaklf";
String anotherString = "asdjrlkweg";
// special types, think before using
dynamic something = "Turns off type checking until runtime";
Object anotherThing = "Tells system that it is an object, but don't assume its type";
Dart在變數這方面有個功能稱為Null Safety,除非特別指定,否則變數必須設定個非null
的初始值,否則會出現null dereference error
。
若真的要將變數設為null
,則在宣告變數時,在型態後面加上個問號?
,此資料型態稱為Nullable Type,但須注意的是,包含nullable type的那行程式將無法存取properties及呼叫函式。
int someNum = 0; // Non-nullable type
int? anotherNum; // Nullable type
另外,我們也可以使用late
在宣告變數,這告訴dart:「我們之後必定會在使用這個變數前設定一個非null的值,但不是現在」。
late String description;
void main() {
description = 'adsfjal';
print(description);
}
想要避免一個變數的值在宣告後受到變動,我們可以使用final
或是const
來代替var
,或者是放在變數型態之前。
p.s. Instance variables can be final but not const
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';
包含if
, else
, for
, while
, switch
, break
, continue
這些保留字。這部份與c語言極其相似,故不細談。
另外darts中switch
並不會有fall through,因此若非一個case沒有任何執行內容,否則可省略break
。
if (year >= 2001) {
print('21st century');
} else if (year >= 1901) {
print('20th century');
}
for (final object in flybyObjects) {
print(object);
}
for (int month = 1; month <= 12; month++) {
print(month);
}
while (year < 2016) {
year += 1;
}
除了常見的//
和/*comment*/
以外,dart還有另一種稱為Documentation Comments,長相為///
或/**comment**/
,多用於開發人員所用的文件說明。
/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
///
/// Just like any other animal, llamas need to eat,
/// so don't forget to [feed] them some [Food].
class Llama {
String? name;
/// Feeds your llama [food].
///
/// The typical llama eats one bale of hay per week.
void feed(Food food) {
// ...
}
/// Exercises your llama with an [activity] for
/// [timeLimit] minutes.
void exercise(Activity activity, int timeLimit) {
// ...
}
}
以上程式範例中,在class所生成的說明文檔documentation中,[feed]會成為一個連結連接到feed的函式,[Food]則會成為一個連結連接到Food這個class。
宣告方式與c相同。雖然不是必要,但建議指定函式回傳值跟argument的資料型態。
int fibonacci(int n) {
if (n == 0 || n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
var result = fibonacci(20);
若函式內僅有一行,可使用=> expr;
代替{ return expr; }
,而=>
又可被稱為arrow syntax。
note: Only expressions can appear between the arrow (=>) and the semicolon (;). Expressions evaluate to values.
int addOne(int num) {
return num+1;
}
int addTwo(int num) => num+2;
函式可以談的內容非常多,未避免此篇文太過冗長,更多詳細內容請見此連結。
用於使用其他libraries的API。在dart中,每個dart檔案都是一個library。
// Importing core libraries, starts with "dart"
import 'dart:math';
// Importing libraries from external packages, starts with "package"
import 'package:test/test.dart';
// Importing files
import 'path/to/my_other_file.dart';
倘若有libraries的物件名稱之間有衝突(如相同名稱),可使用as
來設定使用該library所用的前綴。
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
很可能有些人不太熟悉什麼事Lazy Loading,筆者也是在接觸Neovim後才知道這個詞的。Lazy Loading(a.k.a. Deferred Loading)是僅在一個Library的物件被使用時,才會載入它,這可以加速我們最初開啟程式所需要的時間。
Dart的Lazy Loading僅支援web使用者,這點須注意。
欲lazy load一個library,我們須在import使用deferred as
,而要使用該library時,則用loadLibrary()載入內容。
import 'package:greetings/hello.dart' deferred as hello;
Future<void> greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
更多詳細說明,見此連結。
以下程式範例為一個包含3個properties,2個constructors,以及1個函式。其中,launchYear這個property無法從class外面存取,不能直接設定它的值,因此它是由getter method
來宣告,而非變數的形式。
更多內容說明,請見此連結
class Spacecraft {
String name;
DateTime? launchDate;
// Read-only non-final property
int? get launchYear => launchDate?.year;
// Constructor, with syntactic sugar for assignment to members.
Spacecraft(this.name, this.launchDate) {
// Initialization code goes here.
}
// Named constructor that forwards to the default one.
Spacecraft.unlaunched(String name) : this(name, null);
// Method.
void describe() {
print('Spacecraft: $name');
// Type promotion doesn't work on getters.
var launchDate = this.launchDate;
if (launchDate != null) {
int years = DateTime.now().difference(launchDate).inDays ~/ 365;
print('Launched: $launchYear ($years years ago)');
} else {
print('Unlaunched');
}
}
}
最基本的enumerator長的像這樣:
enum Color { red, green, blue };
Dart的enum可以像class有各種不同field、函示及建構式。宣告方法與class類似,但有著更多需求限制:
final
const
index
, hashCode
, ==
values
,因已經自動生成了一個名為getter
的static valueenum Vehicle implements Comparable<Vehicle> {
car(tires: 4, passengers: 5, carbonPerKilometer: 400),
bus(tires: 6, passengers: 50, carbonPerKilometer: 800),
bicycle(tires: 2, passengers: 1, carbonPerKilometer: 0);
const Vehicle({
required this.tires,
required this.passengers,
required this.carbonPerKilometer,
});
final int tires;
final int passengers;
final int carbonPerKilometer;
int get carbonFootprint => (carbonPerKilometer / passengers).round();
bool get isTwoWheeled => this == Vehicle.bicycle;
@override
int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint;
}
async
與await
可增加程式的閱讀性,並避免*Callback Hell*。
更多詳細說明見此連結
const oneSecond = Duration(seconds: 1);
// the following methods are equivalent
Future<void> printWithDelay(String message) async {
await Future.delayed(oneSecond);
print(message);
}
Future<void> printWithDelay(String message) {
return Future.delayed(oneSecond).then((_) {
print(message);
});
}
與c相似,使用try
, on
, catch
, finally
, throw
,亦有著rethrow
讓caller可看到exception。
更多範例見此連結
if (astronauts == 0) => throw StateError('No astronauts.');
Future<void> describeFlybyObjects(List<String> flybyObjects) async {
try {
for (final object in flybyObjects) {
var description = await File('$object.txt').readAsString();
print(description);
}
} on IOException catch (e) { // can have multiple on
print('Could not describe object: $e');
} finally {
flybyObjects.clear();
}
}
在開發過程中,我們可以使用assert(<condition>, <optionalMessage>);來debug,若condition的布林值是false,會中斷程式的運行。
object
,所有object都是class的instance
var
)null
除非宣告時在變數型態後加上?
Object
;關閉型態驗證直到程式執行時,使用dymamic
List<int>
List<Object>
_
,則該物件是library中的private內容,詳見此連結
_
,後續字元為數字、字母、底線的混合。謝謝閱讀到這裡的讀者,有任何問題或是想說的,都歡迎留言及email~
最後是一些小心得:
我自認為,寫了這麼多,有點像是在複習C語言,實作時自己肯定又會回去看那些說明文件。不過其實心理很高興官方文件寫的這麼簡單明瞭,在查找與學習上非常方便,尤其是入門教學文章末尾的重點整理,那真的幫我一下子複習完剛剛讀到的所有內容。